"""
iOS Appium WebView 支持模块

为iOS自动化框架提供基于 Appium 的 WebView 交互能力
直接使用 Appium 的 iOS WebView 功能，提供更可靠的 WebView 交互
支持自动启动和停止 Appium 服务器
"""
import time
import os
import subprocess
import socket
import atexit
from typing import Any, List, Dict
import pathlib


# 配置日志
from lib.logger import LoggerManager

logger = LoggerManager.get_logger(__name__, write_to_file=False)

try:
    from appium import webdriver
    from appium.webdriver.common.appiumby import AppiumBy
    from appium.webdriver.webdriver import WebDriver as AppiumWebDriver
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    from selenium.common.exceptions import NoSuchElementException, TimeoutException
    from selenium.webdriver.remote.webelement import WebElement
except ImportError:
    logger.error("未安装 Appium Python 客户端，请先安装: pip install Appium-Python-Client")
    logger.error("如果已安装但仍然报错，请检查导入路径")


class IOSAppiumWebView:
    """
    iOS Appium WebView 支持类，提供与WebView交互的能力

    直接使用 Appium 的 iOS WebView 功能，提供更可靠的 WebView 交互
    支持自动启动和停止 Appium 服务器
    """

    def __init__(self, device_id: str = None, bundle_id: str = None, appium_port: int = 4723,
                 auto_start_appium: bool = True, wda_port: int = 8100, use_usb_client: bool = True):
        """
        初始化 Appium WebView 支持类

        Args:
            device_id: iOS 设备 ID (UDID)，如果为 None 则使用连接的第一个设备
            bundle_id: 应用程序包 ID，如果为 None 则使用 Safari
            appium_port: appium server 端口号
            auto_start_appium: 是否自动启动appium server并连接

        """
        self.device_id = device_id
        self.bundle_id = bundle_id
        self.driver:webdriver.Remote = None
        self.wait = None
        self._current_context = 'NATIVE_APP'
        self.appium_process = None
        self.appium_port = appium_port
        self.wda_port = wda_port
        self.use_usb_client = use_usb_client
        atexit.register(self._cleanup)
        logger.info(f"初始化 iOS Appium WebView 支持，设备 ID: {device_id or '默认'}, 应用包 ID: {self.bundle_id}")
        if auto_start_appium:
            self.connect()

    def _cleanup(self):
        """清理资源，终止 Appium 服务器和驱动"""
        try:
            # 关闭驱动
            if self.driver:
                logger.info("关闭 Appium 驱动")
                try:
                    self.driver.quit()
                except Exception as e:
                    logger.warning(f"关闭 Appium 驱动时出错: {e}")
                finally:
                    self.driver = None

            # 终止 Appium 服务器
            if self.appium_process:
                logger.info("终止 Appium 服务器")
                try:
                    # 尝试正常终止
                    self.appium_process.terminate()
                    self.appium_process.wait(timeout=5)
                except subprocess.TimeoutExpired:
                    # 如果超时，强制终止
                    logger.warning("Appium 服务器未能正常终止，强制终止")
                    self.appium_process.kill()
                except Exception as e:
                    logger.warning(f"终止 Appium 服务器时出错: {e}")
                finally:
                    self.appium_process = None
        except Exception as e:
            logger.error(f"清理资源失败: {e}")

    def _start_appium_server(self) -> bool:
        """
        启动 Appium 服务器

        Returns:
            bool: 是否启动成功
        """
        try:
            # 检查端口是否被占用
            with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
                if s.connect_ex(('localhost', self.appium_port)) == 0:
                    logger.info(f"端口 {self.appium_port} 已被占用，假设 Appium 服务器已在运行")
                    return True

            log_path = pathlib.Path(__file__).parents[3] / "logs" / "appium.log"
            # 构建启动命令
            cmd = [
                "appium",
                "--base-path", "/wd/hub",
                "--port", str(self.appium_port),
                "--log-level", "info:debug",
                "--log", str(log_path),
            ]

            logger.info(f"启动 Appium 服务器: {' '.join(cmd)}")

            # 启动 Appium 服务器
            self.appium_process = subprocess.Popen(
                cmd,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True
            )

            # 等待服务器启动
            start_time = time.time()
            while time.time() - start_time < 30:  # 30秒超时
                with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
                    if s.connect_ex(('localhost', self.appium_port)) == 0:
                        logger.info(f"Appium 服务器已启动，监听端口 {self.appium_port}")
                        time.sleep(2)  # 额外等待，确保服务器完全就绪
                        return True
                time.sleep(1)

            # 如果超时，检查进程状态
            if self.appium_process.poll() is not None:
                stderr = self.appium_process.stderr.read()
                logger.error(f"Appium 服务器启动失败: {stderr}")
                self.appium_process = None
                return False

            logger.error("Appium 服务器启动超时")
            return False
        except Exception as e:
            logger.error(f"启动 Appium 服务器失败: {e}")
            return False

    def _is_app_running(self, bundle_id: str) -> bool:
        """
        检查应用是否在运行

        Args:
            bundle_id: 应用程序包 ID

        Returns:
            bool: 应用是否在运行
        """
        try:
            if not self.device_id:
                logger.warning("未指定设备 ID，无法检查应用是否在运行")
                return False

            # 使用 idevicesyslog 检查应用是否在运行
            import subprocess
            cmd = ["idevicesyslog", "-u", self.device_id, "-p", bundle_id, "-t", "0"]
            process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            try:
                # 等待一小段时间，如果有输出则表示应用在运行
                process.wait(timeout=1)
                return process.returncode == 0
            except subprocess.TimeoutExpired:
                # 如果超时，说明应用可能在运行
                process.terminate()
                return True
        except Exception as e:
            logger.warning(f"检查应用是否在运行时出错: {e}")
            return False

    def connect(self) -> bool:
        """
        连接到 Appium 服务器并启动应用

        Args:
            appium_server: Appium 服务器地址，如果为 None 则使用默认地址
            auto_start: 是否自动启动 Appium 服务器

        Returns:
            bool: 是否连接成功
        """
        try:

            if not self._start_appium_server():
                logger.error("无法自动启动 Appium 服务器")
                return False
            appium_server = f"http://localhost:{self.appium_port}/wd/hub"

            app_running = self._is_app_running(self.bundle_id)
            logger.info(f"应用 {self.bundle_id} 是否在运行: {app_running}")
            # 准备 Appium 所需的 Capabilities
            capabilities = {
                'platformName': 'iOS',
                "appium:automationName":"XCUITest",
                'appium:bundleId': self.bundle_id,
                "appium:options": {
                    # 'newCommandTimeout': 2,  # 命令超时时间，单位秒
                    'noReset': True,  # 不重置应用状态
                    'fullReset': False,  # 不完全重置
                    'skipAppKill': True,  # 不杀死应用
                    'shouldTerminateApp': False,  # 测试结束时不终止应用
                    'autoLaunch': not app_running,  # 如果应用未运行，则启动它；否则不启动
                    'webviewCacheTerminationDisabled': True,  # 忽略不重要的视图，提高性能
                    "startIWDP": True,
                    'usePrebuiltWkWebView': True,
                    'autoAcceptAlerts': False,  # 禁用自动接受弹窗
                    'autoDismissAlerts': False,  # 禁用自动拒绝弹窗
                }
            }
            # 如果使用 USB 方式连接，添加 webDriverAgentUrl
            if self.use_usb_client and self.device_id:
                wda_url = f"http+usbmux://{self.device_id}:{self.wda_port}"
                # capabilities["appium:options"]["webDriverAgentUrl"] = wda_url
                capabilities["appium:options"]["skipServerInstallation"] = True
                capabilities["appium:options"]["useNewWDA"] = False
                logger.info(f"使用 USB 方式连接 WebDriverAgent: {wda_url}")
            else:
                # 使用端口转发方式
                wda_url = f"http://localhost:{self.wda_port}"
                capabilities["appium:options"]["webDriverAgentUrl"] = wda_url
                capabilities["appium:options"]["wdaLocalPort"] = self.wda_port
                capabilities["appium:options"]["useNewWDA"] = False
                logger.info(f"使用端口转发方式连接 WebDriverAgent: {wda_url}")

            # # 如果提供了设备 ID，添加到 capabilities
            if self.device_id:
                capabilities["appium:options"]["udid"] = self.device_id

            logger.info(f"连接到 Appium 服务器: {appium_server}")
            logger.info(f"使用 capabilities: {capabilities}")
            # 创建 Appium 驱动
            self.driver = webdriver.Remote(appium_server, capabilities)
            self.wait = WebDriverWait(self.driver, 5,0.5,[TimeoutError])
            # # 设置全局超时
            # self.driver.set_script_timeout(5)  # 设置脚本超时为10秒，而不是默认的120秒
            # self.driver.implicitly_wait(0)      # 设置隐式等待为0秒，避免查找元素时等待太久

            logger.info("连接成功")
            return True
        except Exception as e:
            logger.error(f"连接失败: {e}")
            return False

    def get_contexts(self) -> List[str]:
        """
        获取当前可用的上下文列表

        Returns:
            list: 上下文列表，例如 ['NATIVE_APP', 'WEBVIEW_1']
        """
        try:
            if not self.driver:
                logger.error("未连接到 Appium 服务器")
                return ['NATIVE_APP']

            # 使用安全执行方法获取上下文

            contexts = self.driver.contexts
            if not contexts:
                contexts = ['NATIVE_APP']

            return contexts
        except Exception as e:
            raise e

    def current_context(self) -> str:
        """
        获取当前上下文

        Returns:
            str: 当前上下文名称
        """
        try:
            if not self.driver:
                logger.error("未连接到 Appium 服务器")
                return 'NATIVE_APP'

            # 使用安全执行方法获取当前上下文
            context = self.driver.current_context
            if context:
                self._current_context = context
            else:
                context = 'NATIVE_APP'
                self._current_context = context

            logger.info(f"当前上下文: {context}")
            return context
        except Exception as e:
            logger.error(f"获取当前上下文失败: {e}")
            return 'NATIVE_APP'

    def switch_to_context(self, context_name: str='-1') -> bool:
        """
        切换到指定上下文

        Args:
            context_name: 上下文名称，例如 'NATIVE_APP' 或 'WEBVIEW_1'

        Returns:
            bool: 是否切换成功
        """
        if not self.driver:
            logger.error("未连接到 Appium 服务器")
            return False

        try:
            # 如果当前已经在目标上下文中，不需要切换
            current = self.current_context()
            if current == context_name:
                logger.debug(f"已经在上下文 {context_name} 中，无需切换")
                return True

            # 检查目标上下文是否存在

            contexts = self.get_contexts()
            if context_name == '-1':
                self.driver.switch_to.context(contexts[-1])
                logger.info(f"成功切换到上下文: {contexts[-1]}")
            else:
                if context_name not in contexts:
                    logger.warning(f"上下文 {context_name} 不存在，可用上下文: {contexts}")
                    return False
                # 执行上下文切换
                try:
                    self.driver.switch_to.context(context_name)
                except Exception as e:
                    logger.error(f"切换上下文失败: {e}")
                    return False
                self._current_context = context_name
                logger.info(f"成功切换到上下文: {context_name}")
                return True
        except Exception as e:
            logger.error(f"切换上下文失败: {e}")
            return False

    def switch_to_native(self) -> bool:
        """
        切换到原生上下文

        Returns:
            bool: 是否切换成功
        """
        return self.switch_to_context('NATIVE_APP')

    def switch_to_webview_by_title(self, title: str) -> bool:
        """
        通过标题查找WebView上下文

        Args:
            title: WebView标题

        Returns:
            bool: 是否切换成功
        """
        logger.warning(f"通过TITLE方式切换webview,目前不稳定，会触发appium底层弹窗验证，导致延迟时间太长!")
        try:
            if not self.driver:
                logger.error("未连接到 Appium 服务器")
                return False

            # 获取可用上下文
            contexts = self.get_contexts()

            # 查找WebView上下文
            wcs = [ctx for ctx in contexts if ctx.startswith('WEBVIEW_')]

            if not wcs:
                logger.error("没有可用的WebView上下文")
                return False

            logger.info(f"尝试查找标题为 '{title}' 的WebView")

            # 保存当前上下文
            original_context = self.current_context()

            # 遍历所有WebView上下文，查找匹配标题的页面
            for context in wcs:
                try:
                    # 使用直接上下文切换方法，避免 Appium 的内部弹窗检查
                    self.driver.switch_to.context(context)
                    # 获取当前页面标题
                    current_title = self.driver.title

                    logger.info(f"上下文 {context} 的标题: {current_title}")

                    # 如果标题匹配，返回成功
                    if current_title and title.lower() in current_title.lower():
                        logger.info(f"找到匹配标题 '{title}' 的WebView: {context}")
                        return True
                except Exception as e:
                    continue

            # 如果没有找到匹配标题的上下文，恢复原始上下文
            logger.warning(f"未找到标题为 '{title}' 的WebView上下文")
            self.switch_to_context(original_context)
            return False
        except Exception as e:
            logger.error(f"通过标题查找WebView失败: {e}")
            # 尝试恢复到原始上下文
            try:
                self.switch_to_native()
            except:
                pass
            return False

    def switch_to_webview_by_element(self, loc: tuple) -> bool:
        """
        通过元素查找方式切换到特定的WebView上下文

        Args:
            loc: 元素定位器，格式为 (定位方式, 定位值)，例如 ("xpath", "//div[@id='login']")
            timeout: 元素查找超时时间，单位为秒，默认5秒

        Returns:
            bool: 是否切换成功
        """
        logger.warning(f"通过查找元素方式切换webview,目前不稳定，会触发appium底层弹窗验证，导致延迟时间太长!")
        try:
            if not self.driver:
                logger.error("未连接到 Appium 服务器")
                return False

            # 获取可用上下文
            contexts = self.get_contexts()

            # 查找WebView上下文
            wcs = [ctx for ctx in contexts if ctx.startswith('WEBVIEW_')]

            if not wcs:
                logger.error("没有可用的WebView上下文")
                return False

            logger.info(f"尝试通过元素查找: '{loc[1]}' (类型: {loc[0]})")
            # 遍历所有WebView上下文，查找包含指定元素的页面
            for context in wcs:

                self.driver.switch_to.context(context)

                try:
                    self.driver.find_element(loc[0], loc[1])
                    logger.info(f"在上下文 {context} 中找到元素 {loc[1]}")
                    return True
                except Exception as e:
                    error_str = str(e)
                    # 检查错误是否与 alert 相关
                    if 'no such alert' in error_str or 'An attempt was made to operate on a modal dialog' in error_str:
                        logger.debug(f"忽略 alert 相关错误: {error_str}")
                        # 尝试再次切换，但不处理 alert
                        continue

            # 如果没有找到包含指定元素的上下文，恢复原始上下文
            logger.warning(f"未找到包含元素 '{loc[1]}' 的WebView上下文")
            return False
        except Exception as e:
            logger.error(f"通过元素查找切换WebView失败: {e}")
            return False

    def get_webview_details(self) -> List[Dict]:
        """
        获取所有WebView上下文的详细信息

        Returns:
            List[Dict]: 每个WebView的详细信息，包括上下文ID、标题和URL
        """
        try:
            if not self.driver:
                logger.error("未连接到 Appium 服务器")
                return []

            # 获取可用上下文
            contexts = self.get_contexts()

            # 查找WebView上下文
            webview_contexts = [ctx for ctx in contexts if ctx.startswith('WEBVIEW_')]

            if not webview_contexts:
                logger.info("没有可用的WebView上下文")
                return []

            # 保存当前上下文
            original_context = self.current_context()

            # 收集所有WebView的详细信息
            details = []
            for context in webview_contexts:
                try:
                    # 切换到当前WebView上下文
                    if not self.switch_to_context(context):
                        logger.warning(f"无法切换到上下文: {context}")
                        details.append({
                            "context": context,
                            "title": "无法访问",
                            "url": "无法访问"
                        })
                        continue

                    # 获取页面信息
                    try:
                        title = self.driver.title
                    except:
                        title = "无法获取"

                    try:
                        url = self.driver.current_url
                    except:
                        url = "无法获取"

                    details.append({
                        "context": context,
                        "title": title,
                        "url": url
                    })

                    logger.info(f"上下文: {context}, 标题: '{title}', URL: {url}")
                except Exception as e:
                    logger.warning(f"获取上下文 {context} 的详细信息时出错: {e}")
                    details.append({
                        "context": context,
                        "title": f"错误: {str(e)}",
                        "url": f"错误: {str(e)}"
                    })

            # 恢复原始上下文
            self.switch_to_context(original_context)

            return details
        except Exception as e:
            logger.error(f"获取WebView详细信息失败: {e}")
            return []

    def execute_script(self, script: str, *args) -> Any:
        """
        在当前上下文执行JavaScript脚本

        Args:
            script: JavaScript脚本
            *args: 脚本参数

        Returns:
            Any: 脚本执行结果
        """
        try:
            if not self.driver:
                logger.error("未连接到 Appium 服务器")
                return None

            # 检查当前是否在WebView上下文
            current = self.current_context()
            if not current.startswith('WEBVIEW_'):
                logger.warning(f"当前不在WebView上下文: {current}")
                return None

            # 执行脚本
            result = self.driver.execute_script(script, *args)
            logger.info(f"执行脚本成功: {script[:50]}...")
            return result
        except Exception as e:
            logger.error(f"执行脚本失败: {e}")
            return None

    # WebView 导航方法
    def web_navigate_to(self, url: str) -> bool:
        """
        在WebView中导航到指定URL

        Args:
            url: 目标URL

        Returns:
            bool: 是否导航成功
        """
        try:
            if not self.driver:
                logger.error("未连接到 Appium 服务器")
                return False

            # 检查当前是否在WebView上下文
            current = self.current_context()
            if not current.startswith('WEBVIEW_'):
                logger.warning(f"当前不在WebView上下文: {current}")
                return False

            # 导航到URL
            logger.info(f"导航到URL: {url}")
            self.driver.get(url)

            # 等待页面加载
            self.web_wait_for_page_load()

            logger.info(f"导航到URL成功: {url}")
            return True
        except Exception as e:
            logger.error(f"导航到URL失败: {e}")
            return False

    def web_get_url(self) -> str:
        """
        获取当前WebView的URL

        Returns:
            str: 当前URL
        """
        try:
            if not self.driver:
                logger.error("未连接到 Appium 服务器")
                return ""

            # 检查当前是否在WebView上下文
            current = self.current_context()
            if not current.startswith('WEBVIEW_'):
                logger.warning(f"当前不在WebView上下文: {current}")
                return ""

            # 获取URL
            url = self.driver.current_url
            logger.info(f"当前URL: {url}")
            return url
        except Exception as e:
            logger.error(f"获取URL失败: {e}")
            return ""

    def web_get_title(self) -> str:
        """
        获取当前WebView的标题

        Returns:
            str: 当前标题
        """
        try:
            if not self.driver:
                logger.error("未连接到 Appium 服务器")
                return ""

            # 检查当前是否在WebView上下文
            current = self.current_context()
            if not current.startswith('WEBVIEW_'):
                logger.warning(f"当前不在WebView上下文: {current}")
                return ""

            # 获取标题
            title = self.driver.title
            logger.info(f"当前标题: {title}")
            return title
        except Exception as e:
            logger.error(f"获取标题失败: {e}")
            return ""

    def web_back(self) -> bool:
        """
        在WebView中后退

        Returns:
            bool: 是否后退成功
        """
        try:
            if not self.driver:
                logger.error("未连接到 Appium 服务器")
                return False

            # 检查当前是否在WebView上下文
            current = self.current_context()
            if not current.startswith('WEBVIEW_'):
                logger.warning(f"当前不在WebView上下文: {current}")
                return False

            # 后退
            logger.info("WebView后退")
            self.driver.back()

            # 等待页面加载
            self.web_wait_for_page_load()

            logger.info("WebView后退成功")
            return True
        except Exception as e:
            logger.error(f"WebView后退失败: {e}")
            return False

    def web_forward(self) -> bool:
        """
        在WebView中前进

        Returns:
            bool: 是否前进成功
        """
        try:
            if not self.driver:
                logger.error("未连接到 Appium 服务器")
                return False

            # 检查当前是否在WebView上下文
            current = self.current_context()
            if not current.startswith('WEBVIEW_'):
                logger.warning(f"当前不在WebView上下文: {current}")
                return False

            # 前进
            logger.info("WebView前进")
            self.driver.forward()

            # 等待页面加载
            self.web_wait_for_page_load()

            logger.info("WebView前进成功")
            return True
        except Exception as e:
            logger.error(f"WebView前进失败: {e}")
            return False

    def web_refresh(self) -> bool:
        """
        刷新WebView

        Returns:
            bool: 是否刷新成功
        """
        try:
            if not self.driver:
                logger.error("未连接到 Appium 服务器")
                return False

            # 检查当前是否在WebView上下文
            current = self.current_context()
            if not current.startswith('WEBVIEW_'):
                logger.warning(f"当前不在WebView上下文: {current}")
                return False

            # 刷新
            logger.info("刷新WebView")
            self.driver.refresh()

            # 等待页面加载
            self.web_wait_for_page_load()

            logger.info("刷新WebView成功")
            return True
        except Exception as e:
            logger.error(f"刷新WebView失败: {e}")
            return False

    def web_wait_for_page_load(self, timeout: int = 30) -> bool:
        """
        等待WebView页面加载完成

        Args:
            timeout: 超时时间，单位秒

        Returns:
            bool: 是否加载完成
        """
        try:
            if not self.driver:
                logger.error("未连接到 Appium 服务器")
                return False

            # 检查当前是否在WebView上下文
            current = self.current_context()
            if not current.startswith('WEBVIEW_'):
                logger.warning(f"当前不在WebView上下文: {current}")
                return False

            # 使用 Appium 的 WebDriverWait 等待页面加载
            logger.info(f"等待页面加载，超时时间: {timeout}秒")

            # 等待 document.readyState 为 complete
            WebDriverWait(self.driver, timeout).until(
                lambda driver: driver.execute_script("return document.readyState") == "complete"
            )

            logger.info("页面加载完成")
            return True
        except TimeoutException:
            logger.error(f"等待页面加载超时: {timeout}秒")
            return False
        except Exception as e:
            logger.error(f"等待页面加载失败: {e}")
            return False

    def web_wait_for_element(self, selector: str, timeout: int = 30, selector_type: str = 'css') -> bool:
        """
        等待WebView元素出现

        Args:
            selector: 元素选择器
            timeout: 超时时间，单位为秒，默认30秒
            selector_type: 选择器类型，可以是任何 Appium/Selenium 支持的查找方式

        Returns:
            bool: 元素是否出现
        """
        try:
            if not self.driver:
                logger.error("未连接到 Appium 服务器")
                return False

            # 检查当前是否在WebView上下文
            current = self.current_context()
            if not current.startswith('WEBVIEW_'):
                logger.warning(f"当前不在WebView上下文: {current}")
                return False

            # 等待元素出现
            logger.info(f"等待元素出现: {selector} (类型: {selector_type}), 超时: {timeout}秒")
            WebDriverWait(self.driver, timeout).until(
                EC.presence_of_element_located((selector_type, selector))
            )

            logger.info(f"元素已出现: {selector}")
            return True
        except TimeoutException:
            logger.warning(f"等待元素出现超时: {selector}")
            return False
        except Exception as e:
            logger.error(f"等待元素出现失败: {e}")
            return False

    def web_find_element(self, loc: tuple):
        """
        在WebView中查找元素，使用 Appium 内置方法

        Args:
            loc: 元素定位器，格式为 (定位方式, 定位值)，例如 ("xpath", "//div[@id='login']")
            timeout: 元素查找超时时间，单位为秒，默认5秒
            use_js: 是否使用 JavaScript 查找元素，已弃用，保留参数是为了兼容性

        Returns:
            Any: 元素对象，如果未找到则返回 None
        """
        # 使用重试机制执行查找操作
        # def _find_element():
        try:
            # 设置超时

            logger.info(f"查找元素: {loc[1]} (类型: {loc[0]})")
            # element = self.driver.find_element(selector_type, loc[1])
            element: WebElement = self.wait.until(EC.visibility_of_element_located(loc))
            return element

        except NoSuchElementException:
            logger.info(f"未找到元素: {loc[1]}")
            return None
        except TimeoutError as e:
            logger.error(f"查找元素超时: {loc[1]}")
        except Exception as e:
            logger.error(f"查找元素失败: {loc[1]}, 错误: {str(e)}")
            return None

        # 使用重试机制执行查找操作
        # return self._execute_with_retry(_find_element, max_retries=2, retry_interval=0.5)

    def web_find_elements(self, loc: tuple, timeout: int = 5, use_js: bool = True):
        """
        在WebView中查找多个元素，使用 Appium 内置方法

        Args:
            loc: 元素定位器，格式为 (定位方式, 定位值)，例如 ("xpath", "//div[@class='item']")
            timeout: 元素查找超时时间，单位为秒，默认5秒
            use_js: 是否使用 JavaScript 查找元素，已弃用，保留参数是为了兼容性

        Returns:
            List[Any]: 元素对象列表
        """
        try:
            if not self.driver:
                logger.error("未连接到 Appium 服务器")
                return []

            # 设置超时
            original_timeout = self.driver.timeouts.implicit_wait
            self.driver.implicitly_wait(timeout)

            try:
                # 使用 Appium 内置方法查找元素
                logger.info(f"查找多个元素: {loc[1]} (类型: {loc[0]})")
                elements: list[WebElement] = self.driver.find_elements(loc[0], loc[1])
                return elements
            finally:
                # 恢复原始超时设置
                self.driver.implicitly_wait(original_timeout)
        except Exception as e:
            logger.error(f"查找多个元素失败: {e}")
            return []

    def web_click(self, selector: str, selector_type: str = 'css'):
        """
        点击WebView中的元素

        Args:
            selector: 元素选择器
            selector_type: 选择器类型，可以是任何 Appium/Selenium 支持的查找方式

        Returns:
            bool: 是否点击成功
        """
        # 使用重试机制执行点击操作
        def _click_element():
            try:
                # 查找元素
                element = self.web_find_element((selector_type, selector))
                if not element:
                    logger.warning(f"未找到元素: {selector}")
                    return False

                # 点击元素
                logger.info(f"点击元素: {selector}")
                element.click()
                logger.info("点击成功")
                return True
            except Exception as e:
                logger.error(f"点击元素失败: {e}")
                raise

        # 使用重试机制执行点击操作
        return self._execute_with_retry(_click_element, max_retries=3, retry_interval=1)

    def web_input_text(self, selector: str, text: str, selector_type: str = 'css'):
        """
        在WebView中的输入框中输入文本

        Args:
            selector: 元素选择器
            text: 要输入的文本
            selector_type: 选择器类型，可以是任何 Appium/Selenium 支持的查找方式

        Returns:
            bool: 是否输入成功
        """
        # 使用重试机制执行输入操作
        def _input_text():
            try:
                # 查找输入框元素
                element = self.web_find_element((selector_type, selector))
                if not element:
                    logger.warning(f"未找到输入框元素: {selector}")
                    return False

                # 清除现有文本
                try:
                    element.clear()
                except Exception as e:
                    logger.warning(f"清除文本失败: {e}")

                # 输入文本
                logger.info(f"在元素 {selector} 中输入文本: {text}")
                element.send_keys(text)
                logger.info("输入文本成功")
                return True
            except Exception as e:
                logger.error(f"输入文本失败: {e}")
                raise

        # 使用重试机制执行输入操作
        return self._execute_with_retry(_input_text, max_retries=3, retry_interval=1)

    def web_clear_text(self, selector: str, selector_type: str = 'css'):
        """
        清除WebView中输入框的文本

        Args:
            selector: 元素选择器
            selector_type: 选择器类型，可以是任何 Appium/Selenium 支持的查找方式

        Returns:
            bool: 是否清除成功
        """
        try:
            if not self.driver:
                logger.error("未连接到 Appium 服务器")
                return False

            # 检查当前是否在WebView上下文
            current = self.current_context()
            if not current.startswith('WEBVIEW_'):
                logger.warning(f"当前不在WebView上下文: {current}")
                return False

            # 查找输入框元素
            element = self.web_find_element((selector_type, selector))
            if not element:
                logger.warning(f"未找到输入框元素: {selector}")
                return False

            # 清除文本
            logger.info(f"清除元素 {selector} 中的文本")
            element.clear()

            logger.info(f"清除文本成功: {selector}")
            return True
        except Exception as e:
            logger.error(f"清除文本失败: {e}")
            return False

    def web_get_text(self, selector: str, selector_type: str = 'css'):
        """
        获取WebView元素的文本内容

        Args:
            selector: 元素选择器
            selector_type: 选择器类型，可以是任何 Appium/Selenium 支持的查找方式

        Returns:
            str: 元素文本内容
        """
        try:
            if not self.driver:
                logger.error("未连接到 Appium 服务器")
                return ""

            # 检查当前是否在WebView上下文
            current = self.current_context()
            if not current.startswith('WEBVIEW_'):
                logger.warning(f"当前不在WebView上下文: {current}")
                return ""

            # 查找元素
            element = self.web_find_element((selector_type, selector))
            if not element:
                logger.warning(f"未找到元素: {selector}")
                return ""

            # 获取文本
            text = element.text
            logger.info(f"获取元素 {selector} 的文本: {text}")
            return text
        except Exception as e:
            logger.error(f"获取元素文本失败: {e}")
            return ""

    def web_get_attribute(self, selector: str, attribute: str, selector_type: str = 'css'):
        """
        获取WebView元素的属性值

        Args:
            selector: 元素选择器
            attribute: 属性名
            selector_type: 选择器类型，可以是任何 Appium/Selenium 支持的查找方式

        Returns:
            str: 属性值
        """
        try:
            if not self.driver:
                logger.error("未连接到 Appium 服务器")
                return ""

            # 检查当前是否在WebView上下文
            current = self.current_context()
            if not current.startswith('WEBVIEW_'):
                logger.warning(f"当前不在WebView上下文: {current}")
                return ""

            # 查找元素
            element = self.web_find_element((selector_type, selector))
            if not element:
                logger.warning(f"未找到元素: {selector}")
                return ""

            # 获取属性值
            value = element.get_attribute(attribute)
            logger.info(f"获取元素 {selector} 的属性 {attribute}: {value}")
            return value or ""
        except Exception as e:
            logger.error(f"获取元素属性失败: {e}")
            return ""

    def web_is_element_present(self, selector: str, selector_type: str = 'css', timeout: int = 5, use_js: bool = True) -> bool:
        """
        检查WebView元素是否存在

        Args:
            selector: 元素选择器
            selector_type: 选择器类型，可以是任何 Appium/Selenium 支持的查找方式
            timeout: 超时时间，单位为秒，默认5秒
            use_js: 是否使用 JavaScript 检查，已弃用，保留参数是为了兼容性

        Returns:
            bool: 元素是否存在
        """
        try:
            if not self.driver:
                logger.error("未连接到 Appium 服务器")
                return False

            # 检查当前是否在WebView上下文
            current = self.current_context()
            if not current.startswith('WEBVIEW_'):
                logger.warning(f"当前不在WebView上下文: {current}")
                return False

            # 等待元素出现
            logger.info(f"等待元素出现: {selector} (类型: {selector_type}), 超时: {timeout}秒")
            try:
                WebDriverWait(self.driver, timeout).until(
                    EC.presence_of_element_located((selector_type, selector))
                )
                return True
            except TimeoutException:
                logger.warning(f"等待元素出现超时: {selector}")
                return False
        except Exception as e:
            logger.error(f"检查元素是否存在失败: {e}")
            return False

    def web_is_element_visible(self, selector: str, selector_type: str = 'css', timeout: int = 5, use_js: bool = True) -> bool:
        """
        检查WebView元素是否可见

        Args:
            selector: 元素选择器
            selector_type: 选择器类型，可以是任何 Appium/Selenium 支持的查找方式
            timeout: 超时时间，单位为秒，默认5秒
            use_js: 是否使用 JavaScript 检查，已弃用，保留参数是为了兼容性

        Returns:
            bool: 元素是否可见
        """
        try:
            if not self.driver:
                logger.error("未连接到 Appium 服务器")
                return False

            # 检查当前是否在WebView上下文
            current = self.current_context()
            if not current.startswith('WEBVIEW_'):
                logger.warning(f"当前不在WebView上下文: {current}")
                return False

            # 等待元素出现
            logger.info(f"等待元素出现: {selector} (类型: {selector_type}), 超时: {timeout}秒")
            try:
                element = WebDriverWait(self.driver, timeout).until(
                    EC.presence_of_element_located((selector_type, selector))
                )
                return element.is_displayed()
            except TimeoutException:
                logger.warning(f"等待元素出现超时: {selector}")
                return False
        except Exception as e:
            logger.error(f"检查元素是否可见失败: {e}")
            return False

    def web_take_screenshot(self, filename: str = None) -> str:
        """
        在WebView中截图

        Args:
            filename: 截图文件名，如果为None则自动生成

        Returns:
            str: 截图文件路径
        """
        try:
            if not self.driver:
                logger.error("未连接到 Appium 服务器")
                return ""

            # 检查当前是否在WebView上下文
            current = self.current_context()
            if not current.startswith('WEBVIEW_'):
                logger.warning(f"当前不在WebView上下文: {current}")
                return ""

            # 生成文件名
            if not filename:
                timestamp = time.strftime("%Y%m%d_%H%M%S")
                filename = f"webview_screenshot_{timestamp}.png"

            # 截图
            logger.info(f"WebView截图: {filename}")
            self.driver.save_screenshot(filename)

            # 获取绝对路径
            filepath = os.path.abspath(filename)
            logger.info(f"截图保存到: {filepath}")
            return filepath
        except Exception as e:
            logger.error(f"WebView截图失败: {e}")
            return ""

    def _handle_alerts_in_native(self):
        """
        在原生上下文中处理可能存在的弹窗
        """
        try:
            # 使用较短的隐式等待时间
            original_implicit_wait = self.driver.timeouts.implicit_wait
            self.driver.implicitly_wait(0.5)  # 设置很短的等待时间，避免长时间等待

            try:
                # 使用 find_elements 而不是 find_element，避免抛出异常
                alerts = self.driver.find_elements(AppiumBy.XPATH, "//XCUIElementTypeAlert")
                if alerts and len(alerts) > 0:
                    logger.info("检测到弹窗，尝试关闭")
                    buttons = self.driver.find_elements(AppiumBy.XPATH, "//XCUIElementTypeAlert//XCUIElementTypeButton")
                    if buttons and len(buttons) > 0:
                        buttons[0].click()
                        logger.info("已关闭弹窗")
                        # 给一点时间让弹窗消失
                        time.sleep(0.5)
            except Exception as e:
                logger.debug(f"检查弹窗时出现异常，但这通常是正常的: {e}")
            finally:
                # 恢复隐式等待时间
                self.driver.implicitly_wait(original_implicit_wait)
        except Exception as e:
            logger.debug(f"处理弹窗时出现异常，但继续执行: {e}")

    def _execute_with_retry(self, func, max_retries=3, retry_interval=1, *args, **kwargs):
        """
        使用重试机制执行函数，提高稳定性

        Args:
            func: 要执行的函数
            max_retries: 最大重试次数
            retry_interval: 重试间隔（秒）
            *args, **kwargs: 传递给函数的参数

        Returns:
            函数的返回值，如果所有重试都失败则返回None
        """
        last_exception = None
        for i in range(max_retries):
            try:
                if i > 0:
                    logger.info(f"重试 {func.__name__} ({i}/{max_retries})...")
                    time.sleep(retry_interval)

                return func(*args, **kwargs)
            except Exception as e:
                last_exception = e
                logger.warning(f"执行 {func.__name__} 失败: {e}，将重试...")
                continue

        logger.error(f"执行 {func.__name__} 失败，已达到最大重试次数: {max_retries}")
        return None



if __name__ == '__main__':
    # 创建 Appium WebView 实例
    w = IOSAppiumWebView(device_id="00008140-000E553A118B001C", bundle_id="com.yonyou.upesnAlpha")
    w.connect()
    # w.switch_to_webview_by_element_with_timeout(("xpath", "//div[text()='统计']"),timeout=5)
    w.switch_to_context()
    e = w.web_find_element(("xpath", "//div[text()='统计']"))
    print(e.text)
